package org.jheuristics.ga.operators;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.epanetgrid.otimizacao.log.AGLogger;
import org.jheuristics.Individual;
import org.jheuristics.ga.GAConfig;
import org.jheuristics.ga.GAStatus;
import org.jheuristics.ga.IndividualsFactory;
import org.jheuristics.ga.Operator;
import org.jheuristics.ga.Population;


/**
 * TODO
 *
 * @author Marcell Manfrin, marcell@ourgrid.org, Jan 9, 2006
 */
public class ShiftOperator implements Operator {
    private PopulationChooser chooser;
    private Collection individualsCollection = null;
    private int offSet;

    private AGLogger logger = AGLogger.getInstance();
    
    /**
     * TODO
     *
     * @param populacao
     * @param chooser
     * @param offSet
     */
    public ShiftOperator(Collection populacao, PopulationChooser chooser, int offSet) {
        if (offSet > 0) {
    		this.offSet = offSet;
            setPopulationChooser(chooser);
            individualsCollection = populacao;
    	}
    	else {
    		throw new IllegalArgumentException("O par�metro precisa ser maior que zero.");
    	}
    }

    /**
     * TODO
     *
     * @param status
     * @param config
     * @see org.jheuristics.ga.Operator#operate(org.jheuristics.ga.GAStatus, org.jheuristics.ga.GAConfig)
     */
    public void operate(GAStatus status, GAConfig config) {
    	logger.debug("************SHIFT****************************** offSet= "+offSet);
		
        if (individualsCollection.size() > 0) {
            synchronized (this) {
                Population toPop = chooser.choose(status, config);
                toPop.clear();
                for (Iterator it = individualsCollection.iterator(); it.hasNext(); ) {
                    Individual individual = (Individual) it.next();
                    logger.debug("\noriginal:      "+individual);
                    
                    Individual shiftado = shift(individual, config, offSet);
                    
                    logger.debug("\nshiftado: "+shiftado);
                    
                    toPop.addIndividual(shiftado);
                }
                individualsCollection.clear();
            }
        }
    }
    
    /**
     * Esse m�todo faz um shift para esquerda nos genes atuais do indiv�duo passado como
     * parametro. O shift � feito, copiando o primeiro indiv�duo para a ultima posi��o da
     * lista de genes do indiv�duo atual. � preciso criar um novo genes e um novo indiv�duo 
     * porque os genes e indiv�duos s�o imut�veis.
     *
     * @param individual O indiv�duo cujo genes deseja-se fazer um shift.
     * @param config 
     * @return Um novo indiv�duo com os genes do indiv�duo passado como parametro, por�m com o shift.
     */
    private Individual shift(Individual individual, GAConfig config, int offSet ) {
        IndividualsFactory factory = config.getIndividualsFactory();
        List genesNovos = (List) individual.getGens();
        for (int i = 0; i < offSet; i++) {
            Object genesToRemove = (Object) genesNovos.remove(0);
            genesNovos.add(genesToRemove);
        }
        
        return factory.createSpecificIndividual(genesNovos, config);
    	
    }

    /**
     * TODO
     *
     * @return
     */
    public PopulationChooser getPopulationChooser() {
        return chooser;
    }

    /**
     * TODO
     *
     * @param chooser
     */
    public void setPopulationChooser(PopulationChooser chooser) {
        if (null == chooser) {
            // TODO: ERROR MSG
            throw new NullPointerException();
        }
        this.chooser = chooser;
    }
    
}
